package demo1;
//泛型实现了将数据类型作为参数进行传递
//一般的类和方法，只能使用具体类型。那么如果对于不同类型要做同一个操作，那么需要重新定义
//泛型：就是适用于很多类型。从代码上看，对类型实现了参数化


import java.lang.reflect.Array;

public class Test {

    public static void main(String[] args) {

        A a = new A();
        a.setObj(0,10);
        a.setObj(1,"hello");
        a.setObj(2,10.0);

        //double d = a.getPos(2);  报错，返回值是一个Object，需要用户自己强转
        double d = (double)a.getPos(2);
        //代码不通用，使用时还需要程序员自己强转

        //存在两个问题
        //1---》存储数据时，什么数据都能存储---》拿出来的不知道什么
        //2---》此时获取数据的时候，必须要强转
        //能不能就是指定放啥就可以不强转了
    }

}
//<T> 代表当前类是一个泛型类
//将泛型类看作是一个生产罐子的模型
//实例化泛型类就是制造罐子并给罐子贴标签说明这个罐子存储什么的过程
//尖括号中可以定义多个T E G，
class B<T> {
    public Object[] obj = new Object[3];

    public Object getPos(int pos) {
        return obj[pos];
    }

    public void setPos(int pos,Object val) {
        obj[pos] = val;
    }
}

class C<T> {
    public T[] obj = (T[])new Object[3];

    public T getPos (int pos) {
        return obj[pos];
    }

    public void setPos(int pos,T val) {
        obj[pos] = val;
    }

    public static void main(String[] args) {
        C<Integer> c = new C<>();
        //实例化对象的同时指定当前泛型类的指定数据类型是Interger
        c.setPos(1,23);
        //c.setPos(0."haha");  报错，只能存储指定数据类型 Interger
        //< > 指定的参数类型必须是引用类型，不能是基本类型

        C<String> c1 = new C<>();
        c1.setPos(0,"鲤鱼");
        c1.setPos(1,"haha");

        Integer d = c.getPos(1);
        Integer d1 = c.getPos(0);

        String str1 = c1.getPos(1);
        String str2 = c1.getPos(0);

        //泛型的优点：（编译时）
        //存储数据可以帮我们进行自动的类型检查
        //获取元素的时候，可以帮我们进行类型转换

        //泛型在编译时才存在，在运行是没有泛型的概念
        //像传参一样把数据类型传给对象



    }
}

//定义一个类，类当中包含一个数组，数组可以存放任意类型的数据。
//也可以根据成员方法返回数组下标的值
//object是所有类的父类，那么可以用OBject来接收所有的类（发生向上转型）
//泛型的目的：指定你当前的容器，要存储的数据的类型






//类名后的<T> 代表占位符，表示当前类是一个泛型类
//泛型形参一般使用大写字母表示，常用的名称
//E -->Element  K--->Key  V--->Value
// N--->Number  T-->Type
//不能用new泛型类型的数组  T[] t = new T[]；泛型当中不能去实例化泛型类型的数组

//  T[] obj = (T[])new Object[];  不是正确写法，但是胜在清晰
//正确的写法  Object[] obj = new Object[];
//在实例化泛型类< 类型 >指定泛型实例化对象类型
//实例化的泛型类使用时不需要强制类型转换


//泛型目前的优点：数据类型参数化 编译时自动进行类型检查和转换
//java是怎么编译的---》擦除机制
//编译的时候：自动类型检查+类型转换
//编译完成之后，会将泛型擦除成object
//在编译时生效，在运行时失效


//在编译过程中，将所有的E替换成Object的这种机制，我们称为擦除机制
//java的泛型类是在编译级别实现的，编译器生成的字节码在运行期间并不包含泛型类
//的类型信息

//提出问题:
//为什么 T[] obj = new T[] 会报错？
//既然在编译后会将T擦除为Object ，那编译后不是 Object[] obj = new Object[]

//我们的目的是想要一个具体类型的数据


class F < E > {
    public Object[] obj = new Object[3];

//    E[] obj2 = new E[2];             错误写法，会报错
//    E[] obj3 = (E[])new Object[2];   错误写法，不会报错，存在问题
//    写一个方法，返回数组
//    public E[] getArray() {
//        return obj3;
//    }
//
//    Integer[] integers = new F<Integer> f.getArray();   报错，类型转换错误，（是Object，所以不能赋值给E[]）强转之后还是报错，类型转换异常。
//    Object[] 数组当中不一定全放的是Integer，所以强制类型转换（向下转型）不安全，报错了
//    通过反射机制指定泛型类的数据类型（不常用）
    public E[] obj4;
    public F(Class<E> clazz,int capacity) {
        obj = (E[]) Array.newInstance(clazz,capacity);
    }


    public F() {

    }


    public void setObj(E val) {
        obj[0] = val;
    }

    public  E getObj( ) {
        return (E)obj[0];
    }

    public static void main(String[] args) {
        F f = new F();
        //这种写法应该报错，但是没有报错，主要是为了兼容之前的版本（没有指定类型类的数据类型）
        //裸类型是一个泛型类但是没有带着类型实参，上面的f就是一个裸类型。裸类型
        //是为了兼容之前老版本保留的机制，尽量不要使用
        F<Integer> f1 = new F<>();
        f1.setObj(12);
        f1.setObj(14);
        System.out.println(f.getObj());
//        f1.setObj("heheh");

    }
}


//泛型的上界
//



















class A {
    Object[] obj = new Object[3];

    public Object getPos(int pos) {
        return obj[pos];
    }

    public void setObj(int pos ,Object val) {
        obj[pos] = val;


    }

//    public static void main(String[] args) {
//        char[] chs = {'q','s','s'};
//        System.out.println(chs.toString());
//    }


}
